package com.apes.fn.scm.lossSale;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fn.business.fixedLoss.modle.LossQuotation;
import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailOrder.repository.RetailOrderRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.event.Listener;
import com.apes.framework.plugin.soe.store.database.repository.SceneRepository;
import com.apes.framework.util.LogicalUtil;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.masterdata.enumeration.repository.EnumerationRepository;
import com.apes.scm.sal.saleOrder.SaleOrder;
import com.apes.scm.sal.saleOrder.SaleOrderItem;
import com.apes.scm.sal.saleOrder.SaleOrderItemRepository;
import com.apes.scm.sal.saleOrder.SaleOrderRepository;
import com.apes.scm.stock.transfer.model.Pick;
import com.apes.scm.stock.transfer.model.PickItem;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * @Author wuq
 * @Date 2021-12-16
 */
@Service
public class LossSaleReturnService extends DomainService {

    @Autowired
    private LossSaleReturnRepository lossSaleReturnRepository;
    @Autowired
    private RetailOrderRepository retailOrderRepository;
    @Autowired
    private VoucherRepository voucherRepository;
    @Autowired
    private LossSaleReturnDao lossSaleReturnDao;

    public LossSaleReturn save(SimpleRequest request) {
        LossSaleReturn lossSaleReturn = request.getO(LossSaleReturn.class);
        if (StringUtils.isEmpty(lossSaleReturn.getRetailId())) throw new RuntimeException("零售单号为空，请检查！");

        // 明细数据处理
        Set<LossSaleReturnItem> returnItems = lossSaleReturn.getReturnItems().stream()
                .filter(lossSaleReturnItem -> lossSaleReturnItem.getQtyDelivered() > 0).collect(Collectors.toSet());
        if (returnItems.isEmpty()) throw new RuntimeException("所有商品退回数量全部为0！");
        lossSaleReturn.setReturnItems(returnItems);

        lossSaleReturn.setState("sent");
        lossSaleReturn.setCreator(request.getPersonId());
        lossSaleReturn.setCreateDate(new Date());
        lossSaleReturn.setCompany(lossSaleReturn.getDept().getCompany());
        lossSaleReturn.setRefund("no");     // 暂不支持退款，线下退款，系统不记录

        // 缓存零售单单号
        RetailOrder retailOrder = findRetailOrder(lossSaleReturn.getRetailId());

        lossSaleReturn.setRetailId(retailOrder.getId());
        lossSaleReturn.setCustomerExpand(retailOrder.getCustomerExpand());

        // 配置场景流
        lossSaleReturn.setScene(SpringManager.getBean(SceneRepository.class).findOne("DT001"));

        // 增加新的退回数量
        setQtyReturning(lossSaleReturn, 1.00);

        // 重新计算总计
        double amt = lossSaleReturn.getReturnItems().stream().mapToDouble( LossSaleReturnItem::getPriceTotal ).sum();
        lossSaleReturn.setAmtTotal(amt);

        return lossSaleReturnRepository.saveAndFlush(lossSaleReturn);
    }


    /**
     * 更新分销单中退货冲红数量（退货中/已退货）
     *
     * @param lossSaleReturn
     * @param unit           1/-1
     */
    private void setQtyReturning(LossSaleReturn lossSaleReturn, Double unit) {
        Map<SaleOrderItem, LossSaleReturnItem> map = new HashMap<>();
        lossSaleReturn.getReturnItems().forEach(lossSaleReturnItem -> map.put(getSaleOrderItem(lossSaleReturnItem), lossSaleReturnItem));

        map.keySet().stream().collect(Collectors.groupingBy(SaleOrderItem::getSaleOrder))
                .forEach((saleOrder, orderItems) -> {

                    orderItems.forEach(saleOrderItem -> {
                        double lossReturnQtyDelivered = map.get(saleOrderItem).getQtyDelivered();
                        if (unit != null) { // 新增/修改
                            saleOrderItem.sumQtyReturning(0, unit * lossReturnQtyDelivered);
                        } else {
                            // 交货单完成时，释放提单冲红数量 退货冲红数量  写入退货数量
                            saleOrderItem.sumQtyReturning(0, -1 * lossReturnQtyDelivered);
                            saleOrderItem.calcQtyReturned(lossReturnQtyDelivered);
                        }
                    });
                    SpringManager.getBean(SaleOrderRepository.class).save(saleOrder);
                });
    }

    private SaleOrderItem getSaleOrderItem(LossSaleReturnItem lossSaleReturnItem) {
        if (StringUtils.isEmpty(lossSaleReturnItem.getSaleItemId())) {
            throw new RuntimeException("退货明细行中没有记录对应的分销单明细行号，当前商品：" + lossSaleReturnItem.getProduct().getName());
        }
        return SpringManager.getBean(SaleOrderItemRepository.class).findOne(lossSaleReturnItem.getSaleItemId());
    }

    public LossSaleReturn update(SimpleRequest request) {
        LossSaleReturn newLossSaleReturn = request.getO(LossSaleReturn.class);
        LossSaleReturn oldLossSaleReturn = lossSaleReturnRepository.findOne(newLossSaleReturn.getId());

        // 还原之前的交货数量
        setQtyReturning(oldLossSaleReturn, -1.00);

        // 增加新的交货数量
        setQtyReturning(newLossSaleReturn, 1.00);

        double amt = newLossSaleReturn.getReturnItems().stream().mapToDouble( LossSaleReturnItem::getPriceTotal ).sum();
        newLossSaleReturn.setAmtTotal(amt);

        return lossSaleReturnRepository.saveAndFlush(newLossSaleReturn);
    }

    private RetailOrder findRetailOrder(String retailId) {
        RetailOrder retailOrder = retailOrderRepository.findOne(retailId);
        if (!"RO002".equals(retailOrder.getScene().getId())) throw new RuntimeException("非赋能定损销售来源的单据，不允许调整");
        return retailOrder;
    }

    public String delete(SimpleRequest request) {
        LossSaleReturn lossSaleReturn = request.getO(LossSaleReturn.class);

        LossSaleReturn oldLossSaleReturn = lossSaleReturnRepository.findOne(lossSaleReturn.getId());
        oldLossSaleReturn.setDeleteId(request.getPerson());
        oldLossSaleReturn.setDeleteDate(new Date());
        oldLossSaleReturn.setState("delete");

        // 还原之前的交货数量
        setQtyReturning(oldLossSaleReturn, -1.00);

        return "删除成功";
    }

    /**
     * 根据零售单获取分销单、分销单凭证数据
     *
     * @param request retailId
     * @return Map
     */
    public Map findOriginOrder(SimpleRequest request) {
        String retailId = request.get("retailId");

        // 获取零售单
        RetailOrder retailOrder = findRetailOrder(retailId);

        Map<String, Object> result = new HashMap<>();
        result.put("dept", MapUtil.mapped(retailOrder.getServiceDept()));
        result.put("lossQuotationId", retailOrder.getSource());
        result.put("customerExpand", MapUtil.mapped(retailOrder.getCustomerExpand()));
        result.put("oldInsuranceCompany", MapUtil.mapped(retailOrder.getInsuranceCompany()));

        List<JSONObject> returnItems = findReferenceAccounts(retailOrder.getId());  // 查询相关账目明细
        if (returnItems.isEmpty()) throw new RuntimeException("该单已经没有可退货的商品！");
        result.put("returnItems", returnItems);
        return result;
    }

    public List<JSONObject> findReferenceAccounts(String retailId) {
        List<JSONObject> mapList = new ArrayList<>();

        // 查询分销单
        List<SaleOrder> saleOrders = SpringManager.getBean(SaleOrderRepository.class).findAllBySource(retailId);
        saleOrders.forEach(saleOrder -> {
            saleOrder.getSaleItems().stream()
                    .filter(saleOrderItem -> saleOrderItem.calcQtyDeliveredLimit() > 0)
                    .forEach(saleOrderItem -> {
                Map<String, Object> m = new HashMap<>();

                m.put("saleItemId", saleOrderItem.getId());
                m.put("product", MapUtil.mapped(saleOrderItem.getProduct()));
                m.put("priceUnit", saleOrderItem.getPriceUnit());
                m.put("qtySaleQty", saleOrderItem.getProductUomQty());   // 销售数量
                m.put("qtyReturning", saleOrderItem.getQtyReturning());   // 退货中数量
                m.put("qtyDelivered", 0);   // 退回数量
                m.put("qtyDeliverable", saleOrderItem.calcQtyDeliveredLimit());   // 可退货数量
                m.put("amount", saleOrderItem.getPriceTotal());
                m.put("uomName", saleOrderItem.getUom().getName());
                m.put("location", MapUtil.mapped(saleOrderItem.getSaleOrder().getLocation()));
                mapList.add(new JSONObject(m));
            });
        });
        return mapList;
    }

    public LossSaleReturn approve(SimpleRequest request) {
        String id = request.get("id");
        LossSaleReturn lossSaleReturn = lossSaleReturnRepository.findOne(id);

        lossSaleReturn.setApproveUID(request.getPerson());
        lossSaleReturn.setDateApprove(new Date());
        lossSaleReturn.setState("approve");

        return lossSaleReturnRepository.saveAndFlush(lossSaleReturn);
    }

    public LossSaleReturn confirm(SimpleRequest request) {
        String id = request.get("id");
        LossSaleReturn lossSaleReturn = lossSaleReturnRepository.findOne(id);

        lossSaleReturn.setConfirmId(request.getPerson());
        lossSaleReturn.setConfirmDate(new Date());
        lossSaleReturn.setState("confirm");

        // 如果不需要收货，直接状态改为交货完成
        if ("no".equals(lossSaleReturn.getNeedDelivery())){
            lossSaleReturn.setState("delivered");
        }

        return lossSaleReturnRepository.saveAndFlush(lossSaleReturn);
    }

    /**
     * 创建交货单
     *
     * @param request LossSaleReturn
     * @return Pick
     */
    public List<Pick> createPickOrder(SimpleRequest request) {
        LossSaleReturn lossSaleReturn = request.getO(LossSaleReturn.class);

        List<Pick> pickList = new ArrayList<>();
        //  根据明细上面的仓库进行汇总数据
        lossSaleReturn.getReturnItems().stream()
                .filter(returnItem -> returnItem.getQtyDelivered() > 0)
                .filter(returnItem -> LogicalUtil.notIn(returnItem.getProduct().getType(), "aut"))
                .collect(groupingBy(LossSaleReturnItem::getLocation))
                .forEach(((location, returnItems) -> {

                    Set<PickItem> pickItems = new HashSet<>();
                    returnItems.forEach(
                            returnItem -> {
                                PickItem pickItem = new PickItem();
                                pickItem.setProduct(returnItem.getProduct());
                                pickItem.setUom(returnItem.getProduct().getUom());
                                pickItem.setOrderedQty(returnItem.getQtyDelivered());
                                pickItem.setProductUomQty(returnItem.getQtyDelivered());
                                pickItem.setOriginLine(returnItem.getId());
                                pickItems.add(pickItem);
                            });

                    if (pickItems.isEmpty()) return;

                    Pick pick = new Pick();
                    pick.setMoveType(SpringManager.getBean(EnumerationRepository.class).findOne("one"));
                    pick.setPartner(lossSaleReturn.getCompany().getParty().getPatyRoleByType("Customer"));
                    pick.setLocation(location);
                    pick.setCompany(location.getCompany());     // 公司为库区的公司
                    pick.setAuto(true); // 设置自动交货

                    pick.setOrigin(lossSaleReturn.getId());
                    pick.setOriginBusinessType(lossSaleReturn.getInnerBusinessType());
                    pick.setOperation("receipt");               // 交货类型：收货
                    pick.setScene(lossSaleReturn.getScene());    // 注入场景
                    pick.setPickItems(pickItems);

                    pickList.add(pick);
                }));
        return pickList;
    }


    /**
     * 功能：处理交货完成事件
     * 回写明细的交货数量
     *
     * @param request
     * @return
     */
    @Listener(topic = "event:stock.pick.delivery")
    public void delivery(SimpleRequest request) {
        JSONObject json = request.getJO();
        if (!json.getString("originBusinessType").equals(LossSaleReturn.class.getSimpleName())) return;

        LossSaleReturn order = lossSaleReturnRepository.findOne(json.getString("origin"));
        if (order == null) return;
        for (Map requestItem : ((List<Map>) json.get("items"))) {
            long result = order.getReturnItems().stream()
                    .filter(orderItem -> orderItem.getId().equals((String) requestItem.get("originItemId")))
                    .map(orderItem -> {
                        double qtyDone = ((Number) requestItem.get("qtyDone")).doubleValue();
                        orderItem.delivery(qtyDone);    // 设置交货数量
                        return orderItem;
                    }).count();
            if (result != 1) {
                throw new RuntimeException("零售单行号：" + requestItem.get("originItemId") + " 输入错误！");
            }
        }

        // 一次性交货，更新单据状态
        order.setState("delivered");
        lossSaleReturnRepository.saveAndFlush(order);   // 交货完成
    }


    /**
     * 创建分销凭证
     *
     * @param request LossSaleReturn
     */
    public void processSaleReturn(SimpleRequest request) {
        LossSaleReturn lossSaleReturn = request.getO(LossSaleReturn.class);

        Map<SaleOrderItem, LossSaleReturnItem> map = new HashMap<>();
        lossSaleReturn.getReturnItems().forEach(lossSaleReturnItem -> map.put(getSaleOrderItem(lossSaleReturnItem), lossSaleReturnItem));

        map.keySet().stream().collect(Collectors.groupingBy(SaleOrderItem::getSaleOrder))
                .forEach((saleOrder, orderItems) -> {

                    // 找到对应的原销售凭证
                    String sourceItemId = orderItems.get(0).getId();
                    Voucher originSaleVoucher = voucherRepository.findSaleVoucherByCompanyAndItemSource(saleOrder.getCompany(), saleOrder.getCustomer(), sourceItemId);
                    if (originSaleVoucher == null) throw new RuntimeException("没有找到对应的销售凭证，请检查！");

                    returnSaleVoucher(orderItems, originSaleVoucher, map);
                });


        // 订单完成
        lossSaleReturn.setState("done");
        lossSaleReturnRepository.saveAndFlush(lossSaleReturn);

        // 更新分销单退回数量
        setQtyReturning(lossSaleReturn, null);
    }

    private void returnSaleVoucher(List<SaleOrderItem> saleOrderItems, Voucher originSaleVoucher,
                                   Map<SaleOrderItem, LossSaleReturnItem> param) {
        List<LossSaleReturnItem> lossSaleReturnItem = saleOrderItems.stream().map(param::get).collect(Collectors.toList());
        LossSaleReturn lossSaleReturn = lossSaleReturnItem.get(0).getLossSaleReturn();

        Voucher voucher = new Voucher();
        // 销售退回必填字段
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.SALE_RETURN);
        voucher.setVoucher(originSaleVoucher);

        ReflectUtil.copy(originSaleVoucher, voucher, "dept", "company");
        voucher.setPartner(lossSaleReturn.getCompany().getParty().getPartyRoleByType("Customer"));
        voucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有经销
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：采购

        voucher.setCustomerExpand(lossSaleReturn.getCustomerExpand().getId());
        voucher.setScene(lossSaleReturn.getScene());
        voucher.setOrigin(lossSaleReturn.getLossQuotationId());
        voucher.setOriginBusinessType(LossQuotation.class.getSimpleName());
        voucher.setSource(lossSaleReturn.getId());
        voucher.setSourceBusinessType(lossSaleReturn.getInnerBusinessType());

        voucher.setVoucherItems(
                originSaleVoucher.getVoucherItems().stream().map(saleItem -> {
                    VoucherItem voucherItem = new VoucherItem();

                    lossSaleReturnItem.stream()
                            .filter(returnItem -> returnItem.getProduct().getId().equals(saleItem.getProduct().getId()))
                            .forEach(returnItem -> {

                                voucherItem.setProduct(saleItem.getProduct());
                                voucherItem.setTax(saleItem.getTax());
                                voucherItem.setOwner(originSaleVoucher.getCompany());

                                voucherItem.setUom(saleItem.getUom());          // 设置为销售的单位以及数量
                                voucherItem.setProductQty(-1 * Math.abs(returnItem.getQtyDelivered()));
                                voucherItem.setPriceUnit(saleItem.getPriceUnit());      // 默认为退货单价

                                voucherItem.setOriginItemId(returnItem.getId());
                                voucherItem.setSourceItemId(returnItem.getId());
                                voucherItem.setVoucherItem(saleItem);   // 退回时传入明细行号
                            });

                    return voucherItem;
                }).filter(voucherItem -> voucherItem.getProduct() != null).collect(Collectors.toSet()));

        voucher = invoke("voucher.create", MapUtil.mapped(voucher));
        invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(voucher))));
    }

    public Page<LossSaleReturn> findAllByPage(SimpleRequest request){
        JSONObject param = paramWrapper(request.getJO(), request);
        JSONArray ids = lossSaleReturnDao.findAll(param);
        long count = lossSaleReturnDao.findCount(param).getJSONObject(0).getLongValue("COUNT");
        List<LossSaleReturn> lossSaleReturns = new ArrayList<>();
        if (ids != null && !ids.isEmpty()){
            lossSaleReturns = lossSaleReturnRepository.findAll(JpaDsl.toCriteria("id", "in",
                    ids.stream().map(item -> ((JSONObject) item).getString("ID")).collect(Collectors.toList())));
        }
        return pageWrapper(param, lossSaleReturns,count);
    }

    private Page<LossSaleReturn> pageWrapper(JSONObject param,List<LossSaleReturn> content,Long total){
        Pageable pageable = PageRequest.of(param.getInteger("page"), param.getInteger("size"));
        return new PageImpl<LossSaleReturn>(content, pageable,total);
    }

    private JSONObject paramWrapper(JSONObject requestJO,SimpleRequest request){
        JSONObject param = new JSONObject();
        JSONArray condition = requestJO.getJSONArray("condition");

        double size = requestJO.getDoubleValue("size");
        double page = requestJO.getDoubleValue("page");
        param.put("size",size);
        param.put("page",page);
        param.put("min",(page * size) + 1);
        param.put("max",(page + 1) * size);
        param.put("postId",request.getPostId());

        condition.stream().forEach(o -> {
            JSONObject itemJO = (JSONObject) o;
            if (itemJO.containsKey("type") && itemJO.getString("type").equals("date")){
                param.put(itemJO.getString("field") + itemJO.getString("operator"),itemJO.getDate("value")); // 时间字段封装
            } else {
                param.put(itemJO.getString("field"),itemJO.getString("value")); // 普通字段封装
            }
        });
        return param;
    }
}
